<?php

class Request {

    /**
     * 单例模式
     *
     * @var object
     */
    private static $_instance = null;

    /**
     * uri 参数
     *
     * @var array
     */
    private $_params = array();

    /**
     * uri 原始参数
     *
     * @var array
     */
    private $_raw_params = array();

    /**
     * 安全过滤后的$_POST
     * @var array 
     */
    private $_post = array();

    /**
     * 安全过滤后的$_GET
     * @var array 
     */
    private $_get = array();

    /**
     * 安全过滤后的$_POST、$_GET和Params组合
     * @var array 
     */
    private $_args = array();

    /**
     * 模块
     * 
     * @var string
     */
    private $_module = '';

    /**
     * 控制器
     * 
     * @var string
     */
    private $_controller = '';

    /**
     * 动作
     * 
     * @var string
     */
    private $_action = '';

    /**
     * 构造函数,但该类不能被实例化
     */
    private function __construct() {
        // 处理请求数据，处理反斜杠转义
        $this->_post = $_POST = $this->_magicQuotes($_POST);
        $this->_get = $_GET = $this->_magicQuotes($_GET);
        $this->_args = array_merge($this->_get, $this->_post);
    }

    /**
     * 生成单个实例
     *
     * @return Request object
     */
    static public function getInstance() {
        if (null === self::$_instance) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    /**
     * 得到输入的参数变量 $_SERVER['argv']数组中的变量
     *
     * @param int $offset:域名称,空则返回整个数组
     * @param string $default:不存在时的默认值
     * @return mixed
     */
    public function argv($offset = null, $default = null) {
        if (null === $offset) {
            return $_SERVER['argv'];
        } else {
            return (isset($_SERVER['argv']) && isset($_SERVER['argv'][$offset])) ? $_SERVER['argv'][$offset] : $default;
        }
    }

    /**
     * 得到文件域 $_FILES 数组
     *
     * @param int $offset:域名称
     * @return mixed
     */
    public function files($name = '') {
        if (!$name) {
            return $_FILES;
        } else {
            return isset($_FILES[$name]) ? $_FILES[$name] : null;
        }
    }

    /**
     * 得到全局变量 $_SERVER 的值
     *
     * @param string $name
     * @param string $default:不存在时的默认值
     * @return string
     */
    public function server($name, $default = null) {
        return isset($_SERVER[$name]) ? $_SERVER[$name] : $default;
    }

    /**
     * 设置 POST 的值
     *
     * @param mixed $name:域名称
     * @param string $value:值
     */
    public function setPost($name, $value = '') {
        if (!is_array($name)) {
            $this->_args[$name] = $this->_post[$name] = $_POST[$name] = $this->_magicQuotes($value);
        } else {
            $_POST = array_merge($_POST, $this->_magicQuotes($name));
            $this->_post = array_merge($this->_post, $this->_magicQuotes($name));
            $this->_args = array_merge($this->_args, $this->_magicQuotes($name));
        }
        return $this;
    }

    /**
     * 获得 POST 数据
     *
     * @param string $name:域名称,如果为空则返回整个 $_POST 数组
     * @param string $default:不存在时的默认值
     * @return mixed
     */
    public function getPost($name = '', $default = null) {
        return $name ? (isset($this->_post[$name]) ? $this->_post[$name] : $default) : $this->_post;
    }

    /**
     * 获得 Uri 参数
     * @param string $name:域名称,如果为空则返回所有参数数组
     * @param string $default:不存在时的默认值
     * @return mixed
     */
    public function getParam($name = '', $default = null) {
        return $name ? (isset($this->_params[$name]) ? $this->_params[$name] : $default) : $this->_params;
    }

    /**
     * 获得未安全过滤的参数
     * @return mixed
     */
    public function rawParam($name = '', $default = null) {
        return $name ? (isset($this->_raw_params[$name]) ? $this->_raw_params[$name] : $default) : $this->_raw_params;
    }

    /**
     * 设置参数值
     *
     * @param mixed $name:域名称
     * @param string $value:值
     */
    public function setParam($name, $value = '') {
        if (!is_array($name)) {
            $this->_args[$name] = $this->_params[$name] = $this->_magicQuotes($value);
        } else {
            $this->_params = array_merge($this->_params, $this->_magicQuotes($name));
            $this->_args = array_merge($this->_args, $this->_magicQuotes($name));
        }
        return $this;
    }

    /**
     * 设置 GET 的值
     *
     * @param mixed $name:域名称
     * @param string $value:值
     */
    public function setGet($name, $value = '') {
        if (!is_array($name)) {
            $this->_args[$name] = $this->_get[$name] = $_GET[$name] = $this->_magicQuotes($value);
        } else {
            $_GET = array_merge($_GET, $this->_magicQuotes($name));
            $this->_get = array_merge($this->_get, $this->_magicQuotes($name));
            $this->_args = array_merge($this->_args, $this->_magicQuotes($name));
        }
        return $this;
    }

    /**
     * 获得 GET 数据
     *
     * @param string $name:域名称,如果为空则返回整个 $_GET 数组
     * @param string $default:不存在时的默认值
     * @return mixed
     */
    public function getGet($name = '', $default = null) {
        return $name ? (isset($this->_get[$name]) ? $this->_get[$name] : $default) : $this->_get;
    }

    /**
     * 按POST/GET/Params的优先顺序返回数据
     * @param string $name:域名称
     * @param string $default:不存在时的默认值
     * @return mixed
     */
    public function get($name = '', $default = null) {
        return $name ? (isset($this->_args[$name]) ? $this->_args[$name] : $default) : $this->_args;
    }

    /**
     * 返回模块
     * 
     * @return string
     */
    public function getModule() {
        return $this->_module;
    }

    /**
     * 返回控制器
     * 
     * @return string
     */
    public function getController() {
        return $this->_controller;
    }

    /**
     * 返回动作
     * 
     * @return string
     */
    public function getAction() {
        return $this->_action;
    }

    /**
     * 设置模块
     * 
     * @param string $module
     */
    public function setModule($module) {
        $this->_module = $module;
    }

    /**
     * 设置控制器
     * 
     * @param string $controller
     */
    public function setController($controller) {
        $this->_controller = $controller;
    }

    /**
     * 设置动作
     * 
     * @param string $action
     */
    public function setAction($action) {
        $this->_action = $action;
    }

    /**
     * 判断当前请求是否为 POST
     *
     * @return bool
     */
    public function isPost() {
        return $_SERVER['REQUEST_METHOD'] == 'POST';
    }

    /**
     * 返回上一个页面的 URL 地址(来源)
     *
     * @return string
     */
    public function frontUrl() {
        return isset($_SERVER["HTTP_REFERER"]) ? str_replace(':443', '', $_SERVER["HTTP_REFERER"]) : '';
    }

    /**
     * 返回当前页面的 URL 地址
     *
     * @return string
     */
    public function currentUrl() {
        return $this->serverLocation() . $this->requestUri();
    }

    /**
     * 取得当前请求使用的协议
     *
     * 返回值不包含协议的版本。常见的返回值是 http。
     * @return string 当前请求使用的协议
     */
    public function protocol() {
        static $protocol;
        if (is_null($protocol)) {
            list ($protocol) = explode('/', $_SERVER['SERVER_PROTOCOL']);
        }
        return strtolower($protocol);
    }

    /**
     * 确定请求的完整 URL
     * 
     * @return string 请求的完整 URL
     */
    public function requestUri() {
        if (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
            $uri = $_SERVER['HTTP_X_REWRITE_URL'];
        } elseif (isset($_SERVER['REQUEST_URI'])) {
            $uri = $_SERVER['REQUEST_URI'];
        } elseif (isset($_SERVER['ORIG_PATH_INFO'])) {
            $uri = $_SERVER['ORIG_PATH_INFO'];
            if (!empty($_SERVER['QUERY_STRING'])) {
                $uri .= '?' . $_SERVER['QUERY_STRING'];
            }
        } else {
            $uri = '';
        }

        // 兼容 pathinfo 如: /index.php/index/test
        return strpos($uri, '/index.php') === 0 ? substr($uri, 10) : $uri;
    }

    /**
     * 确定请求的完整路径
     * 
     * @return string 请求的完整路径
     */
    public function requestPath() {

        // 获取URL
        $request_uri = $this->requestUri();

        // 去掉所有的 GET 参数
        $p = strpos($request_uri, '?');
        if (false !== $p) {
            $request_uri = substr($request_uri, 0, $p);
        }

        return $request_uri;
    }

    /**
     * 返回服务器响应请求使用的端口
     *
     * 通常服务器使用 80 端口与客户端通信，该方法可以获得服务器所使用的端口号。
     *
     * @return string 服务器响应请求使用的端口
     */
    public function serverPort() {
        static $server_port = null;
        if ($server_port)
            return $server_port;
        if (isset($_SERVER['SERVER_PORT'])) {
            $server_port = intval($_SERVER['SERVER_PORT']);
        } else {
            $server_port = 80;
        }

        if (isset($_SERVER['HTTP_HOST'])) {
            $arr = explode(':', $_SERVER['HTTP_HOST']);
            $count = count($arr);
            if ($count > 1) {
                $port = intval($arr[$count - 1]);
                if ($port != $server_port) {
                    $server_port = $port;
                }
            }
        }
        return $server_port;
    }

    /**
     * 返回服务器地址
     * 
     * @return string 带协议、域名和端口号的完整服务器地址
     */
    public function serverLocation() {
        $port = $this->serverPort();
        $host = $this->server('HTTP_HOST');
        $protocol = $this->protocol();

        if ($protocol == 'http') {
            if ($this->isHttps()) {
                $protocol = 'https';
            }
        }

        if ($port == 80 || $port = 443) {
            $location = $protocol . '://' . $host;
        } else {
            $location = $protocol . '://' . $host . ':' . $port;
        }
        return $location;
    }

    public function isHttps() {

        if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') {
            return true;
        } elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
            return true;
        } elseif (!empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off') {
            return true;
        }

        return false;
    }

    /**
     * 判断 HTTP 请求是否是通过 XMLHttp 发起的
     *
     * @return boolean
     */
    public function isAjax() {
        return strtolower($this->header('X_REQUESTED_WITH')) == 'xmlhttprequest';
    }

    /**
     * 返回请求的原始内容
     *
     * @return string
     */
    public function rawBody() {
        $body = file_get_contents('php://input');
        return (strlen(trim($body)) > 0) ? $body : false;
    }

    /**
     * 返回 HTTP 请求头中的指定信息，如果没有指定参数则返回 false
     *
     * @param string $header 要查询的请求头参数
     *
     * @return string 参数值
     */
    public function header($header) {
        $temp = 'HTTP_' . strtoupper(str_replace('-', '_', $header));
        if (!empty($_SERVER[$temp]))
            return $_SERVER[$temp];
        if (function_exists('apache_request_headers')) {
            $headers = apache_request_headers();
            if (!empty($headers[$header]))
                return $headers[$header];
        }
        return false;
    }

    /**
     * 得到访问都的IP
     *
     */
    public function getClientIp() {
        if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown"))
            $ip = getenv("HTTP_CLIENT_IP");
        else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown"))
            $ip = getenv("HTTP_X_FORWARDED_FOR");
        else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown"))
            $ip = getenv("REMOTE_ADDR");
        else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown"))
            $ip = $_SERVER['REMOTE_ADDR'];
        else
            $ip = "unknown";
        return($ip);
    }

    /**
     * 根据设置处理魔术引用
     *
     * @param string|array $value 值
     * @return unknown
     */
    private function _magicQuotes($value) {
        // 是否打开了magic_quotes_gpc
        if (phpversion() < '5.3.0' && get_magic_quotes_gpc()) {
            /*
             * 默认情况下，PHP 指令 magic_quotes_gpc 为 on，对所有的 GET、POST 和 COOKIE 数据自动运行 addslashes()。
             * addslashes()：对输入字符串中的某些预定义字符前添加反斜杠，这样处理是为了数据库查询语句等的需要。
             * 这些预定义字符是：单引号 (') ，双引号 (") ，反斜杠 (\) ，NULL。
             * stripslashes()：删除由 addslashes() 函数添加的反斜杠。该函数用于清理从数据库或 HTML 表单中取回的数据。
             */
            $value = is_array($value) ? array_map(array($this, __FUNCTION__), $value) : stripslashes($value);
        }

        return $value;
    }

    /**
     * 解析 URI 中的参数
     * 
     */
    public function parseUri() {

        // 读取模块配置
        $modules = Sys::getCfg('modules');

        if (!$modules || !is_array($modules)) {
            throw new Exception("未定义模块");
        }

        $modules = array_map('strtolower', $modules);

        // 子域名绑定
        if ($host = $this->server('HTTP_HOST')) {

            $hosts = explode(':', $host);
            $host_name = strtolower($hosts[0]);

            // 重新对模块排序，以便把默认模块排到模块组的首位
            $sub_domain_bind = Sys::getCfg('sub_domain_bind');

            if ($sub_domain_bind && is_array($sub_domain_bind)) {

                foreach ($sub_domain_bind as $key => $value) {

                    if (strstr($host_name, $value) === $value) {

                        $bind_module = strtolower($key);

                        unset($modules[array_search($bind_module, $modules)]);

                        array_unshift($modules, $bind_module);

                        break;
                    }
                }
            }
        }

        // 去除前后 "/"
        $request_path = $this->requestPath();

        // 检查是否设置了路由
        if (Route::isUseRoute() && $route = Route::matches($request_path)) {

            $this->setModule($route['module']);
            $this->setController($route['controller']);
            $this->setAction($route['action']);

            // 路由模式下无法解析url中的其他参数
        } else {

            // 去除前后 "/"
            $request_path = trim($request_path, '/');

            // 分解参数,到这时 $request_path 应该是这种形式了: api/index/test
            $params = $request_path ? explode('/', $request_path) : array();

            // 统计参数数量
            $plen = count($params);

            // 设置默认参数 模块、 控制器 和 动作，假设默认模块模块是Api
            if ($plen > 2) {
                /*
                 * /api/a/b -> /api/a/b
                 * /a/b/c -> /api/a/b/c
                 */
                if (!in_array($params[0], $modules)) {
                    array_unshift($params, $modules[0]);
                }
            } elseif ($plen == 2) {
                /*
                 * /api/a -> /api/a/index
                 * /a/b -> /api/a/b
                 */
                if (in_array($params[0], $modules)) {
                    $params[] = Controller::DEFAULT_ACTION;
                } else {
                    array_unshift($params, $modules[0]);
                }
            } elseif ($plen == 1) {
                /*
                 * /mobile -> /api/index/index
                 * /a -> /api/a/index
                 */
                if (in_array($params[0], $modules)) {
                    $params[] = Controller::DEFAULT_CONTROLLER;
                    $params[] = Controller::DEFAULT_ACTION;
                } else {
                    array_unshift($params, $modules[0]);
                    $params[] = Controller::DEFAULT_ACTION;
                }
            } else {
                /*
                 * / -> /api/index/index
                 */
                $params[] = $modules[0];
                $params[] = Controller::DEFAULT_CONTROLLER;
                $params[] = Controller::DEFAULT_ACTION;
            }

            $this->_raw_params = $params; // 保存原始参数

            $this->setModule(ucfirst(strtolower($params[0])));
            $this->setController(ucfirst(strtolower($params[1])));
            $this->setAction(strtolower($params[2]));

            // 清理参数中的前3个参数
            $params = array_slice($params, 3);

            // 没有其它参数就不用解析了
            $plen = count($params);
            if ($plen == 0) {
                return;
            }

            // 解析其它参数 id/1/name/yuanwei
            for ($i = 2; $i <= $plen; $i += 2) {
                $this->_params[$params[$i - 2]] = $params[$i - 1];
            }

            $this->_args = array_merge($this->_params, $this->_args);
        }
    }

}
